package com.hznu.coursemanagerbackend.common.utils;


import com.hznu.coursemanagerbackend.common.exception.OAException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;

/**
 * 文件相关的工具
 *
 * @author Cliven
 * @since 2017-10-18 16:14:27
 */
public final class FileUtils {


    /**
     * 创建文件或者目录，当它们不存在的时候
     *
     * @param fileFullPath 文件或者目录完整路径
     * @return 对应路径的File文件
     * @throws IOException 由于文件相关操作造成的IO异常
     * @author Cliven danbu
     * @since 2017-10-18 21:09:00
     */
    public static File createFile(String fileFullPath) throws IOException {
        File targetFile = new File(fileFullPath);

        // 参数检查
        Assert.isTrue(!StringUtils.isEmpty(fileFullPath), "fileFullPath 为空");
        // 创建file 对象
        // File 对象对象所指向的目录或文件不存时创建目录及文件
        if (!targetFile.exists()) {
            // 从 fileFullPath 解析出上一级目录
            File parentDirection = new File(targetFile.getParent());
            // 上一级目录不存在则直接创建目录
            if (!parentDirection.exists())
                parentDirection.mkdirs();

            // 确保上一级目录存在，然后创建文件
            targetFile.createNewFile();
        }

        return targetFile;
    }

    /**
     * 将 MultipartFile 类型的文件对象转为 File 类型文件对象
     *
     * @param file  MultipartFile 对象
     * @param paths 文件装换时文件路径（可选）
     * @return File 类文件对象
     * @author Petros Tsialiamanis from Stack Overflow
     * @since 2017-10-18 16:20:07
     */
    public static File convert(MultipartFile file, String... paths) {

        // 装换之后生成的最终文件
        String path = paths.length != 0 ? paths[0] : "";

        File convFile = null;
        try {
            convFile = createFile(path + file.getOriginalFilename());
            // 创建文件
            // 创建输出流向文件中写入内容
            FileOutputStream fos = new FileOutputStream(convFile);
            // 向文件中写入File字节
            fos.write(file.getBytes());
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
            convFile = null;
        }
        return convFile;
    }

    /**
     * 读取文件并且获取他的字节串
     *
     * @param file 文件的file对象
     * @return 文件的btye数组
     * @throws IOException IO操作异常/文件过大
     * @author utsengar form github modify By Cliven
     * @since 2017-7-7 12:53:13
     */
    public static byte[] loadFile(File file) throws IOException {
        InputStream is = null;
        byte[] bytes;
        try {
            is = new FileInputStream(file);
            long length = file.length();
            if (length > Integer.MAX_VALUE) {
                // File is too large
                throw new IOException("文件过大");
            }
            bytes = new byte[(int) length];

            int offset = 0;
            int numRead = 0;
            try {
                while (offset < bytes.length
                        && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
                    offset += numRead;
                }
            } catch (IOException e) {
                throw new IOException("文件读入异常");
            }

            if (offset < bytes.length) {
                throw new IOException("不能完全读取文件 " + file.getName());
            }
        } finally {
            closeInputStream(is);
        }

        return bytes;
    }

    /**
     * 获取文件基于Base64 字符串
     *
     * @param path 文件路径
     * @return base64编码过的文件字符串
     * @throws IOException 文件操作造成的IO异常
     * @author Cliven
     * @since 2017-7-7 13:24:07
     */
    public static String getFileBase64(String path) throws IOException {

        String result = null;

        File file = new File(path);

        byte[] rowBytes = loadFile(file);


        byte[] encodedBytes = Base64.encodeBase64(rowBytes);

        // 这可能会造成 OutOfMemory 异常产生
        result = new String(encodedBytes);

        return result;
    }


    /**
     * 把Base64字符串解码,并且写入对应路径位置的文件中去 自动覆盖
     *
     * @param fullPath  文件的完整路径
     * @param base64Str 文件base64编码原文
     * @throws IOException 文件操作造成的一些IO异常
     * @author Cliven
     * @date 2017-7-7 13:28:12
     */
    public static void writeBase64StrToFile(String fullPath, String base64Str) throws IOException {
        if (StringUtils.isEmpty(fullPath))
            throw new NullPointerException("文件存储路径为空");
        OutputStream outputStream = null;
        try {
            if (!Base64.isBase64(base64Str))
                throw new IOException("不是Base64的字符串");

            // 首先创建文件如果不存在
            File file = createFile(fullPath);
            // 直接覆盖文件内容
            outputStream = new FileOutputStream(file);
            // 写入到文件中
            outputStream.write(Base64.decodeBase64(base64Str));
        } catch (IOException e) {
            e.printStackTrace();
            throw new IOException("文件写入IO异常");
        } finally {
            closeOutputStream(outputStream);
        }
    }

    /**
     * 把ByteArrayOutputStream 转换为base64编码过的String
     *
     * @param outputStream 输入的 ByteArrayOutputStream
     * @return base64编码过得字符串
     * @throws IOException 关闭流异常
     * @author Cliven
     * @date 2017-7-8 15:12:23
     */
    public static String OutputStreamToBase64(ByteArrayOutputStream outputStream) throws IOException {
        String result = null;
        if (outputStream == null)
            throw new NullPointerException("输入的 ByteArrayOutputStream 为空");
        try {

            byte[] fileBytes = outputStream.toByteArray();
            fileBytes = Base64.encodeBase64(fileBytes);
            result = new String(fileBytes); // 这个可能造成内存溢出
        } finally {
            closeOutputStream(outputStream);
        }
        return result;
    }


    /**
     * 关闭输出流
     *
     * @param stream 输出流
     * @throws IOException 关闭异常
     * @author Cliven
     * @since 2017-7-8 15:10:22
     */
    private static void closeOutputStream(OutputStream stream) throws IOException {
        try {
            if (stream != null)
                stream.close();
        } catch (IOException e) {
            e.printStackTrace();
            throw new IOException("输出流 IO关闭异常");
        }
    }

    /**
     * 关闭输入流
     *
     * @param stream 输入流
     * @throws IOException 关闭异常
     * @author Cliven
     * @since 2017-7-8 15:10:22
     */
    private static void closeInputStream(InputStream stream) throws IOException {
        try {
            if (stream != null)
                stream.close();
        } catch (IOException e) {
            e.printStackTrace();
            throw new IOException("输入流 IO关闭异常");
        }
    }

    /**
     * function: 获得文件后缀(格式)
     *
     * @date 2018-05-27 20:12:53
     * @author wheelchen
     * @param filename
     * @return
     */
    public static String getSuffix(String filename) {
        if (StringUtils.isBlank(filename)) {
            throw new OAException("文件名为空");
        }
        try {
            return filename.substring(filename.lastIndexOf("."));
        }catch (IndexOutOfBoundsException e) {
            throw new OAException("文件名无格式");
        }

    }

    /**
     * @Description:提供下载模板统一处理,将模板转换成二进制流
     * @param: filepath
     * @return:
     * @author: TateBrown
     * @Date: 2018/12/10
     */
    public  static byte[] ConvertFiletoByte(String filepath)throws IOException{
        try{
            Resource resource = new ClassPathResource(filepath);
            if (resource.exists()) {
                return FileCopyUtils.copyToByteArray(resource.getInputStream());
            } else {
                throw new OAException("文件名无格式");
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new IOException("模板读取异常");
        }
    }

    /**
     * @Description: 将文件转化成二进制数组与资源区别开
     * @param:
     * @return:
     * @author: TateBrown
     * @Date: 2019/1/16
     */
    public static byte[] ConvertFilestoByte(String filepath) throws IOException {
        try {
            File file=new File(filepath);
            if(file.exists()) {
                byte[] data = org.apache.commons.io.FileUtils.readFileToByteArray(file);
                return data;
            }else{
                throw new OAException("文件名无格式");
            }
        }catch(IOException e){
            e.printStackTrace();
            throw new IOException("文件读取异常");
        }
    }
}
